# Copyright (c) 2008 Alvin Wang http://cloudwizard.com/
# License New BSD
# See README for additional information
#
# Author: Greg Hookey
# Email:  <ghookey@deadlysoft.com>
# Date:   February 11, 2009
#

#
#  Ex:  Template ${key} is replaced with ${value}.
#
#       replacement_dict = dict()
#       replacement_dict[ 'key' ] = 'string'
#       replacement_dict[ 'value ] = 'this value'
#
#    One can now prompt a user for a given template value,
#       build a dictionary of replacement values, and then
#       load a template file from disk and save the result to
#       disk or return the result as a string
#

import os

from mako.template import Template
from mako.runtime import Context
from mako import exceptions
from StringIO import StringIO

import tempfile

class CWTemplate( object ):

    # template version
    version = '0.1'

    # the Make template to execute
    cwtemplate = None

    # template contents
    template_contents = ''

    # string buffer for result
    buf = None

    def __init__( self ):
        return

    def load_template_file( self, template_path=None ):
        if not template_path:
            return False

        if not os.path.exists( template_path ) or not os.path.isfile( template_path ):
            print "Template: " + template_path + " does not exist or is not a regular file."
            print "Template loading failed."
            return False

        if self.cwtemplate != None:
            del self.cwtemplate
            self.cwtemplate = None

        try:
            infile = open( template_path, "r" )
            self.template_contents = infile.read()
        except IOError, err:
            print "IO Exception reading: " + template_path + " " + err
        finally:
            infile.close()
            return False

        return True

    def apply_template( self, template_attributes=None ):
        if not template_attributes:
            return False

        if self.buf != None:
            del self.buf
            self.buf = None

        if self.contains_executable_code() == True:
            print "Unable to execute template with unsupported template features."
            return False

        if self.contains_substitution_expressions() == True:
            print "Unable to execute template with unsupported substitution expressions."
            return False

        cwtemplate = Template( self.template_contents )
        tmp = StringIO()
        ctx = Context( tmp, **template_attributes )
        cwtemplate.render_context( ctx )
        self.buf = tmp.getvalue()

        return True

    def save_result( self, result_path=None ):
        """Save Results to a file"""
        if not result_path:
            print "Invalid output path specified."
            return False

        if not self.buf:
            print "Output buffer is empty. Not writing output buffer."
            return False

        try:
            outfile = open( result_path, "w" )
            outfile.write( self.buf )
        except IOError, err:
            print "IO Exception writing: " + result_path + " " + err
        finally:
            outfile.close()
            return False

        return True

    def get_result( self ):
        return self.buf

    def process_file( self, template_file=None, data=None ):
        """Process the template and return a temporary file"""
        tmpfile = tempfile.NamedTemporaryFile()
        newfile = tmpfile.name
        tmpfile.close()
        
        self.load_template_file( template_file )
        self.apply_template( data )
        self.save_result( newfile )
        return newfile
        
    def contains_executable_code( self ):
        #
        # Check for executeable code blocks and other
        # language features
        #
        if not self.template_contents:
            return False
        if self.template_contents.find( "<%" ) == -1:
            return False
        return True

    def contains_substitution_expressions( self ):
        if not self.template_contents:
            return False

        start = 0
        end = 0
        length = len( self.template_contents )

        while start < length:
            start = self.template_contents.find( "${", start )
            if start == -1:
                break
            start += 2
            end = self.template_contents.find( "}", start )
            if end == -1:
                #
                #  Malformed substitution.  Treat that the same way.
                #  We could also just skip this one and look for other
                #  errors in the template.  In either case the user
                #  needs to deal with the problem.
                #
                return True
            #
            # Verify the contents of the substitution is just a variable name
            # and not an expression of any type.  We don't want any external
            # Python code executed in the template.
            #
            for x in range( start, end ):
                if self.template_contents[ x ].isalnum() or self.template_contents[ x ] == '-' or self.template_contents[ x ] == '_':
                    continue
                else:
                    return True
            #
            # advance past end to find next token
            # 
            start = end + 1

        return False
